package com.smile.config;

import com.alibaba.cloud.nacos.discovery.NacosServiceDiscovery;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.event.HeartbeatEvent;
import org.springframework.cloud.gateway.event.RefreshRoutesEvent;
import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
 * 自动注册网关路由配置
 * 通过注册中心的心跳机制实时查询是否注册
 *
 * @author 苦瓜不苦
 * @date 2022/11/15 14:19
 **/
@Slf4j
@Configuration
public class RouteRegisterConfig {
    // nacos服务发现
    private final NacosServiceDiscovery nacosServiceDiscovery;
    private final RouteDefinitionWriter routeDefinitionWriter;
    private final ApplicationEventPublisher publisher;
    // 已注册路由缓存
    private final Map<String, String> registered = new ConcurrentHashMap<>();
    public RouteRegisterConfig(NacosServiceDiscovery nacosServiceDiscovery,
                              RouteDefinitionWriter routeDefinitionWriter,
                              ApplicationEventPublisher publisher) {
        this.nacosServiceDiscovery = nacosServiceDiscovery;
        this.routeDefinitionWriter = routeDefinitionWriter;
        this.publisher = publisher;
    }
    // @EventListener事件监听注解，监听HeartbeatEvent心跳机制
    @EventListener(classes = HeartbeatEvent.class)
    public void eventListen(ApplicationEvent applicationEvent) {
        try {
            System.out.println("收到心跳变化");
            List<String> serviceList = nacosServiceDiscovery.getServices();
            if (serviceList == null || serviceList.isEmpty()) return;
            for (String service : serviceList) {
//                System.out.println("<UNK>" + service);
                if (registered.containsKey(service)) continue;
                List<ServiceInstance> instanceList = nacosServiceDiscovery.getInstances(service);
                if (instanceList == null || instanceList.isEmpty()) continue;
                ServiceInstance instance = instanceList.get(0);
                Map<String, String> metadata = instance.getMetadata();
                if (!"true".equals(metadata.get("route.enable"))) continue;
//                String contextPath = metadata.get("context-path");
                String contextPath = metadata.get("route.path");
                registered.put(service, contextPath);
                if (!StringUtils.hasText(contextPath)) continue;
                String path = contextPath.startsWith("/") ? contextPath : "/" + contextPath;
                path = path.endsWith("/") ? path.substring(0, path.length() - 1) : path;
                // 检查路径是否已包含 **，如果包含则不添加 /**
                String predicatePath = path;
                if (!path.endsWith("/**")) {
                    predicatePath = path + "/**";
                }
                // 构建 RouteDefinition
                RouteDefinition definition = new RouteDefinition();
                definition.setId(service);
                definition.setUri(URI.create("lb://" + service));
                definition.setOrder(0);

                // 断言
                PredicateDefinition predicate = new PredicateDefinition();
                predicate.setName("Path");
                Map<String, String> args = new HashMap<>();
                args.put("_genkey_0", predicatePath);
                predicate.setArgs(args);
                definition.setPredicates(Collections.singletonList(predicate));

                // 注册路由
                routeDefinitionWriter.save(Mono.just(definition)).subscribe();
                publisher.publishEvent(new RefreshRoutesEvent(this));
                registered.put(service, path);
                log.info("服务 {} 自动注册路由成功，path={}，目标=lb://{}", service, path, service);
            }
        } catch (NacosException e) {
            log.error("注册中心心跳机制监听失败\n", e);
        }
    }
}